{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte 3: Ferramentas Avançadas para Execução Remota\n",
    "\n",
    "Na última seção nós utilizamos o conceito de Federated Learning para treinarmos um modelo bem simples. Nós fizemos isto apenas chamando *.send()* e *.get()* em nosso modelo. Ou seja, enviando-o para o local onde se encontram os dados de treinamento, atualizando-o, e então trazendo-o de volta. No entando, ao final do exemplo nós percebemos que precisaríamos ir mais além para proteger a privacidade das pessoas. Isto é, queremos calcular a média dos gradientes **antes** de chamar *.get()*. Dessa forma, nunca veremos o gradiente exato de ninguém (protegendo melhor sua privacidade!!!) \n",
    "\n",
    "Mas, para fazer isso, precisamos de mais algumas coisas:\n",
    "\n",
    "- usar um ponteiro para enviar um Tensor diretamente para outro Worker\n",
    "\n",
    "Além disso, enquanto estivermos aqui, vamos aprender sobre algumas operações mais avançadas com tensores, o que nos ajudará tanto com este exemplo quanto com alguns outros no futuro!\n",
    "\n",
    "Autores:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "Tradução:\n",
    "- Jeferson Silva - Twitter: [@jefersonnpn](https://twitter.com/jefersonnpn)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Seção 3.1 - De ponteiros para ponteiros\n",
    "\n",
    "Como você sabe, os objetos do tipo *PointerTensor* se comportam da mesma forma que tensores normais. De fato, eles são *tão parecidos com tensores* que podemos até ter ponteiros que apontam **para** outros ponteiros. Confira!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob = sy.VirtualWorker(hook, id='bob')\n",
    "alice = sy.VirtualWorker(hook, id='alice')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# este é um tensor local.\n",
    "x = torch.tensor([1,2,3,4])\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# enviando o tensor local para Bob\n",
    "x_ptr = x.send(bob)\n",
    "\n",
    "# agora temos um apontador/ponteiro\n",
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# agora podemos ENVIAR O APONTADOR para alice!!!\n",
    "pointer_to_x_ptr = x_ptr.send(alice)\n",
    "\n",
    "pointer_to_x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Mas o que acabamos de fazer?\n",
    "\n",
    "Bom, no exemplo anterior, nós criamos um tensor chamado `x` e o enviamos para Bob, criando um ponteiro em nossa máquina local (`x_ptr`).\n",
    "\n",
    "Em seguida, nós chamamos `x_ptr.send(alice)` que **envia o ponteiro** para Alice.\n",
    "\n",
    "Note, isso NÃO move os dados! Em vez disso, move o ponteiro, isto é, o apontador para os dados!! "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Como você pode ver acima, Bob ainda possui os dados reais (os dados sempre são armazenados com tipo LocalTensor).\n",
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Alice, por outro lado, tem x_ptr!! (observe o ponteiro para Bob)\n",
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# podemos usar .get() para recuperar x_ptr de Alice\n",
    "\n",
    "x_ptr = pointer_to_x_ptr.get()\n",
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# e então podemos usar x_ptr para recuperar x de Bob!\n",
    "\n",
    "x = x_ptr.get()\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Aritmética em Pointero -> Pointero -> Objeto com Dados\n",
    "\n",
    "Assim como nos ponteiros normais, podemos executar quaisquer operações do PyTorch nesses tensores."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p2p2x = torch.tensor([1,2,3,4,5]).send(bob).send(alice)\n",
    "\n",
    "y = p2p2x + p2p2x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y.get().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p2p2x.get().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Seção 3.2 - Operações com tensores em cadeia\n",
    "\n",
    "Na última seção, sempre que usávamos uma operação *.send() ou *.get()*, chamávamos essas operações diretamente do tensor em nossa máquina local. No entanto, se você tiver uma cadeia de ponteiros, às vezes você deseja chamar essas operações como *.get()* ou *.send()* no **último** ponteiro da cadeia (como enviar dados diretamente de um *worker* para outro). Para conseguirmos isso, você deseja usar funções especialmente projetadas para lidar com operações que devem preservar sua privacidade.\n",
    "\n",
    "Essas operações são:\n",
    "\n",
    "- `meuPonteiro_para_outroPonteiro.mover(outro_worker)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# x agora é um ponteiro para um outro ponteiro para os dados que vivem na máquina de Bob\n",
    "x = torch.tensor([1,2,3,4,5]).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('  bob:', bob._objects)\n",
    "print('alice:', alice._objects)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = x.move(alice)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('  bob:', bob._objects)\n",
    "print('alice:', alice._objects)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Excelente! Agora estamos equipados com as ferramentas para executar a **média de gradientes** remota usando um agregador confiável!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parabéns!!! - Hora de se juntar a comunidade!\n",
    "\n",
    "Parabéns por concluir esta etapa do tutorial! Se você gostou e gostaria de se juntar ao movimento em direção à proteção de privacidade, propriedade descentralizada e geração, demanda em cadeia, de dados em IA, você pode fazê-lo das seguintes maneiras!\n",
    "\n",
    "### Dê-nos uma estrela em nosso repo do PySyft no GitHub\n",
    "\n",
    "A maneira mais fácil de ajudar nossa comunidade é adicionando uma estrela nos nossos repositórios! Isso ajuda a aumentar a conscientização sobre essas ferramentas legais que estamos construindo.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Junte-se ao Slack!\n",
    "\n",
    "A melhor maneira de manter-se atualizado sobre os últimos avanços é se juntar à nossa comunidade! Você pode fazer isso preenchendo o formulário em [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### Contribua com o projeto!\n",
    "\n",
    "A melhor maneira de contribuir para a nossa comunidade é se tornando um contribuidor do código! A qualquer momento, você pode acessar a página de *Issues* (problemas) do PySyft no GitHub e filtrar por \"Projetos\". Isso mostrará todas as etiquetas (tags) na parte superior, com uma visão geral de quais projetos você pode participar! Se você não deseja ingressar em um projeto, mas gostaria de codificar um pouco, também pode procurar mais mini-projetos \"independentes\" pesquisando problemas no GitHub marcados como \"good first issue\".\n",
    "\n",
    "- [Projetos do PySyft](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Etiquetados como Good First Issue](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Doar\n",
    "\n",
    "Se você não tem tempo para contribuir com nossa base de códigos, mas ainda deseja nos apoiar, também pode se tornar um Apoiador em nosso Open Collective. Todas as doações vão para hospedagem na web e outras despesas da comunidade, como hackathons e meetups!\n",
    "\n",
    "[Página do Open Collective do OpenMined](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
